module es {
    /**
     * @ignore
     */
    const _d2r = Math.PI / 180.0;
    /**
     * @ignore
     */
    const _r2d = 180.0 / Math.PI;

    /**
     * @property {number} EPSILON
     */
    export const EPSILON = 0.000001;

    // Number of bits in an integer
    export const INT_BITS = 32;
    export const INT_MAX = 0x7fffffff;
    export const INT_MIN = -1 << (INT_BITS - 1);



    export class Mathf {

        /**
 * Tests whether or not the arguments have approximately the same value, within an absolute
 * or relative tolerance of glMatrix.EPSILON (an absolute tolerance is used for values less
 * than or equal to 1.0, and a relative tolerance is used for larger values)
 *
 * @param {Number} a The first number to test.
 * @param {Number} b The second number to test.
 * @returns {Boolean} True if the numbers are approximately equal, false otherwise.
 */
        public static equals(a, b) {
            return Math.abs(a - b) <= EPSILON * Math.max(1.0, Math.abs(a), Math.abs(b));
        }

        /**
         * 插值
         * @param from 
         * @param to 
         * @param ratio 
         * @returns 
         */
        public static lerp(from, to, ratio) {
            return from + (to - from) * ratio;
        }

        /**
         * 混合
         * @param src 
         * @param dest 
         * @param ration 
         */
        public static mix(src, dest, ration) {
            return Mathf.lerp(src, dest, ration);
        }

        /**
         * Returns a floating-point random number between min (inclusive) and max (exclusive).
         *
         * @method randomRange
         * @param {number} min
         * @param {number} max
         * @return {number} the random number
         */
        public static randomRange(min, max) {
            return Math.random() * (max - min) + min;
        }
        /**
        * Returns a random integer between min (inclusive) and max (exclusive).
        *
        * @method randomRangeInt
        * @param {number} min
        * @param {number} max
        * @return {number} the random integer
        */
        public static randomRangeInt(min, max) {
            return Math.floor(Mathf.randomRange(min, max));
        }

        /**
         * Linear congruential generator using Hull-Dobell Theorem.
         *
         * @method pseudoRandom
         * @param {number} seed the random seed
         * @return {number} the pseudo random
         */
        public static pseudoRandom(seed) {
            seed = (seed * 9301 + 49297) % 233280;
            return seed / 233280.0;
        }
        /**
         * Returns a floating-point pseudo-random number between min (inclusive) and max (exclusive).
         *
         * @method pseudoRandomRange
         * @param {number} seed
         * @param {number} min
         * @param {number} max
         * @return {number} the random number
         */
        public static pseudoRandomRange(seed, min, max) {
            return Mathf.pseudoRandom(seed) * (max - min) + min;
        }
        /**
 * Returns a pseudo-random integer between min (inclusive) and max (exclusive).
 *
 * @method pseudoRandomRangeInt
 * @param {number} seed
 * @param {number} min
 * @param {number} max
 * @return {number} the random integer
 */
        public static pseudoRandomRangeInt(seed, min, max) {
            return Math.floor(Mathf.pseudoRandomRange(seed, min, max));
        }
        /**
 * Returns the next power of two for the value
 *
 * @method nextPow2
 * @param {number} val
 * @return {number} the the next power of two
 */
        public static nextPow2(val) {
            --val;
            val = (val >> 1) | val;
            val = (val >> 2) | val;
            val = (val >> 4) | val;
            val = (val >> 8) | val;
            val = (val >> 16) | val;
            ++val;

            return val;
        }
        /**
         * Returns ratio of a value within a given range
         *
         * @method repeat
         * @param {number} from start value
         * @param {number} to end value
         * @param {number} value given value
         * @return {number} the ratio between [from,to]
         */
        public static inverseLerp(from, to, value) {
            return (value - from) / (to - from);
        }
        /**
         * Returns -1, 0, +1 depending on sign of x.
         * 
         * @method sign
         * @param {number} v
         */
        public static sign(v) {
            return <any>(v > 0) - <any>(v < 0);
        }
        /**
 * Clamps a value between a minimum float and maximum float value.
 *
 * @method clamp
 * @param {number} val
 * @param {number} min
 * @param {number} max
 * @return {number}
 */
        public static clamp(val, min, max) {
            return val < min ? min : val > max ? max : val;
        }


        /**
         * Tests whether or not the arguments have approximately the same value by given maxDiff
         *
         * @param {Number} a The first number to test.
         * @param {Number} b The second number to test.
         * @param {Number} maxDiff Maximum difference.
         * @returns {Boolean} True if the numbers are approximately equal, false otherwise.
         */
        public static approx(a, b, maxDiff) {
            maxDiff = maxDiff || EPSILON;
            return Math.abs(a - b) <= maxDiff;
        }
        /**
        * Convert Degree To Radian
        *
        * @param {Number} a Angle in Degrees
        */
        public static toRadian(a) {
            return a * _d2r;
        }
        /**
* Convert Radian To Degree
*
* @param {Number} a Angle in Radian
*/
        public static toDegree(a) {
            return a * _r2d;
        }

        /**
         * Determine whether an integer n is a power of 2
         * @param n 
         * @returns 
         */
        public static isPow(n) {
            return (n & (n - 1)) == 0;
        }

        /**
         * Gets the unique, non-repeating value of an integer array
         * @param arr 
         * @returns 
         */
        public static findChosenOne(arr: number[], i = 1, res = arr[0]) {
            return arr.length <= i ? res : Mathf.findChosenOne(arr, i + 1, res ^ arr[i]);
        }
    }
}